﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Game.cs" company="Jarno Burger">
//   See copyright.txt in the root of this project.
// </copyright>
// <summary>
//   Defines the Game type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace WinFormAdapter
{
    using System;
    using System.Diagnostics;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Windows.Forms;

    using GUI4UFramework.Management;

    /// <summary>
    /// This is the form that behaves like a game.
    /// </summary>
    public class Game : Form
    {
        /// <summary>
        /// The timer that make the engine Tick.
        /// </summary>
        private readonly Timer timer;

        /// <summary>
        /// The current time used.
        /// </summary>
        private readonly GameTime currentTime;

        /// <summary>To measure currentTime.</summary>
        private readonly Stopwatch stopwatch;

        /// <summary>
        /// The last time that a new image was drawn.
        /// </summary>
        private double lastTimeDrawn;

        /// <summary>The last time that the game logic updated.</summary>
        private double lastTimeUpdated;

        /// <summary>The draw interval (how many times we draw per second).</summary>
        private const float DrawInterval = 1000f / 15f;

        /// <summary>The update interval (how many times we update per second).</summary>
        private const float UpdateInterval = 1000f / 25f;

        /// <summary>
        /// How many times we have updated.
        /// </summary>
        private int updateCount;

        /// <summary>
        /// How many times we have drawn.
        /// </summary>
        private int drawCount;

        /// <summary>
        /// Initializes a new instance of the <see cref="Game"/> class.
        /// </summary>
        protected Game()
        {           
            this.currentTime = new GameTime();
            this.InputManager = new WindowsInputManager(this);

            this.timer = new Timer { Interval = 10 };
            this.timer.Tick += this.OnTimerTick;

            this.stopwatch = new Stopwatch();
            this.stopwatch.Start();

            this.DoubleBuffered = true;
            this.Initialize();

            this.timer.Start();
        }

        /// <summary>
        /// Gets or sets the current bitmap shown.
        /// </summary>
        /// <value>
        /// The current bitmap.
        /// </value>
        public Bitmap CurrentBitmap 
        {
            get; 
            set; 
        }

        /// <summary>
        /// Gets or sets the current graphics device , that draws the stuff on the bitmap.
        /// </summary>
        /// <value>
        /// The current graphics.
        /// </value>
        public Graphics CurrentGraphics
        { 
            get; 
            set;
        }

        /// <summary>
        /// Gets the current time used.
        /// </summary>
        /// <value>
        /// The current time.
        /// </value>
        public GameTime CurrentTime
        {
            get
            {
                return this.currentTime;
            }
        }

        /// <summary>
        /// Gets or sets the input manager, that reads the hardware inputs.
        /// </summary>
        /// <value>
        /// The input manager.
        /// </value>
        public WindowsInputManager InputManager
        {
            get; 
            set; 
        }

        /// <summary>
        /// Gets or sets a value indicating whether the mouse is visible.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is mouse visible; otherwise, <c>false</c>.
        /// </value>
        public bool IsMouseVisible
        {
            get; 
            set;
        }

        /// <summary>
        /// Gets a value indicating whether this <see cref="Game"/> is loaded.
        /// </summary>
        /// <value>
        ///   <c>true</c> if loaded; otherwise, <c>false</c>.
        /// </value>
        public bool Loaded
        {
            get; 
            private set;
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        protected virtual void Initialize()
        {
        }

        /// <summary>
        /// Loads the content for this instance.
        /// </summary>
        protected virtual void LoadContent()
        {
        }

        /// <summary>
        /// Unloads the content for this instance.
        /// </summary>
        protected virtual void UnloadContent()
        {
        }

        /// <summary>
        /// Updates the content for this instance.
        /// </summary>
        /// <param name="gameTime">The game time.</param>
        // ReSharper disable once UnusedParameter.Local
        protected virtual void Update(GameTime gameTime)
        {
            this.updateCount++;
        }

        /// <summary>
        /// Draws the specified game time.
        /// </summary>
        /// <param name="gameTime">The game time.</param>
        // ReSharper disable once UnusedParameter.Local
        protected virtual void Draw(GameTime gameTime)
        {
            this.drawCount++;
        }

        /// <summary>
        /// Called when the timer ticks.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="eventArgs">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void OnTimerTick(object sender, EventArgs eventArgs)
        {
            // get time
            this.currentTime.Update(new TimeSpan(this.stopwatch.ElapsedTicks));
            var cur = this.currentTime.TotalGameTime.TotalMilliseconds;

            // load when needed
            if (this.Loaded == false)
            {
                var bitmap = new Bitmap(Width, Height);
                this.CurrentGraphics = Graphics.FromImage(bitmap);
                this.LoadContent();
                this.BackgroundImage = bitmap.Clone(new Rectangle(0, 0, Width, Height), PixelFormat.DontCare);
                this.CurrentGraphics.Dispose();

                this.Loaded = true;
            }

            // draw when needed
            if (cur - this.lastTimeDrawn > DrawInterval)
            {
                var bitmap = new Bitmap(Width, Height);
                this.CurrentGraphics = Graphics.FromImage(bitmap);
                this.Draw(this.currentTime);
                this.BackgroundImage = bitmap.Clone(new Rectangle(0, 0, Width, Height), PixelFormat.DontCare);
                this.CurrentGraphics.Dispose();

                this.lastTimeDrawn = cur;

                this.Text = string.Format("Updates : {0} Draws : {1}", this.updateCount, this.drawCount);
            }

            // update when needed
            if (cur - this.lastTimeUpdated > UpdateInterval)
            {
                this.Update(this.currentTime);
                this.lastTimeUpdated = cur;
            }
        }
    }
}
